/* Copyright 2022 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { AnnotationEditorType } from "../../shared/util.js";
import { AnnotationEditor } from "./editor.js";
import { noContextMenu } from "../display_utils.js";

/**
 * Basic draw editor in order to generate an Ink annotation.
 */
class ArrowEditor extends AnnotationEditor {
  #rawX = 0;

  #rawY = 0;

  #rawHeight = 0;

  #rawWidth = 0;

  #displayHeight = 0;

  #displayWidth = 0;

  #baseHeight = 0;

  #baseWidth = 0;

  #startX = null;

  #startY = null;

  #endX = null;

  #endY = null;

  #boundCanvasPointermove = this.canvasPointermove.bind(this);

  #boundCanvasPointerleave = this.canvasPointerleave.bind(this);

  #boundCanvasPointerup = this.canvasPointerup.bind(this);

  #boundCanvasPointerdown = this.canvasPointerdown.bind(this);

  #canvasContextMenuTimeoutId = null;

  #disableEditing = false;

  #isCanvasInitialized = false;

  #observer = null;

  #realWidth = 0;

  #realHeight = 0;

  static ARROW_TYPE = {
    // 原点 等于 0
    ORIGIN_POINT: 0,

    // 垂直的 从底部到顶部
    VERTICAL_BTN_TO_TOP: 1,

    // 垂直的 从顶部到底部
    VERTICAL_TOP_TO_BTN: 2,

    // 水平的 从左到右
    HORIZONTAL_LEFT_TO_RIGHT: 3,

    // 水平的 从右到左：
    HORIZONTAL_RIGHT_TO_LEFT: 4,

    // 四象限
    QUADRANT_ONE: 5,

    QUADRANT_TWO: 6,

    QUADRANT_THREE: 7,

    QUADRANT_FOUR: 8,
  };

  // 盒子必须要大一点，才能装下整个线段和箭头
  static _offset_for_arrow = 5;

  static _type = "arrow";

  static _editorType = AnnotationEditorType.ARROW;

  constructor(params) {
    super({ ...params, name: "arrowEditor" });
    if (params.fromCommand) {
      this.fromCommand = params.fromCommand;
      this.#rawX = params.x;
      this.#rawY = params.y;
      this.#rawWidth = params.width;
      this.#rawHeight = params.height;
      this.arrowType = params.arrowType;
      this.#disableEditing = true;
      this.#inferRectFromRaw();
    } else {
      this.fromCommand = false;
      this.x = 0;
      this.y = 0;
      this.arrowType = null;
      this.#rawX = this.#rawY = this.#rawHeight = this.#rawWidth = 0;
    }

    this.scaleFactor = 1;
    this.translationX = this.translationY = 0;
    // 不用控制倍率
    this._willKeepAspectRatio = false;
  }

  #inferRectFromRaw() {
    const [width, height] = this.parentDimensions;

    // x y 要往左上角偏一点，
    this.x = (this.#rawX * width - ArrowEditor._offset_for_arrow) / width;
    this.y = (this.#rawY * height - ArrowEditor._offset_for_arrow) / height;
    // 长宽要各留出一段距离来
    this.width =
      (this.#rawWidth * width + 2 * ArrowEditor._offset_for_arrow) / width;
    this.height =
      (this.#rawHeight * width + 2 * ArrowEditor._offset_for_arrow) / width;
  }

  static initialize(l10n) {
    AnnotationEditor.initialize(l10n);
  }

  rawRect() {
    return {
      x: this.#rawX,
      y: this.#rawY,
      height: this.#rawHeight,
      width: this.#rawWidth,
    };
  }

  rebuild() {
    if (!this.parent) {
      return;
    }
    super.rebuild();
    if (this.div === null) {
      return;
    }

    if (!this.canvas) {
      this.#createCanvas();
      this.#createObserver();
    }

    if (!this.isAttachedToDOM) {
      // At some point this editor was removed and we're rebuilding it,
      // hence we must add it to its parent.
      this.parent.add(this);
      this.#setCanvasDims();
    }
    this.#fitToContent();
  }

  /** @inheritdoc */
  remove(forHide = false) {
    if (this.canvas === null) {
      return;
    }

    if (!this.isEmpty()) {
      this.commit();
    }

    // Destroy the canvas.
    this.canvas.width = this.canvas.height = 0;
    this.canvas.remove();
    this.canvas = null;

    if (this.#canvasContextMenuTimeoutId) {
      clearTimeout(this.#canvasContextMenuTimeoutId);
      this.#canvasContextMenuTimeoutId = null;
    }

    this.#observer.disconnect();
    this.#observer = null;

    super.remove(forHide);
  }

  setParent(parent) {
    if (!this.parent && parent) {
      // We've a parent hence the rescale will be handled thanks to the
      // ResizeObserver.
      this._uiManager.removeShouldRescale(this);
    } else if (this.parent && parent === null) {
      // The editor is removed from the DOM, hence we handle the rescale thanks
      // to the onScaleChanging callback.
      // This way, it'll be saved/printed correctly.
      this._uiManager.addShouldRescale(this);
    }
    super.setParent(parent);
  }

  onScaleChanging() {
    const [parentWidth, parentHeight] = this.parentDimensions;
    const width = this.width * parentWidth;
    const height = this.height * parentHeight;
    this.setDimensions(width, height);
  }

  /** @inheritdoc */
  enableEditMode() {
    if (this.#disableEditing || this.canvas === null) {
      return;
    }

    super.enableEditMode();
    this._isDraggable = false;
    this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown);
  }

  /** @inheritdoc */
  disableEditMode() {
    if (!this.isInEditMode() || this.canvas === null) {
      return;
    }

    super.disableEditMode();
    this._isDraggable = !this.isEmpty();
    this.div.classList.remove("editing");

    this.canvas.removeEventListener(
      "pointerdown",
      this.#boundCanvasPointerdown
    );
  }

  /** @inheritdoc */
  onceAdded() {
    this._isDraggable = !this.isEmpty();
  }

  /** @inheritdoc */
  isEmpty() {
    return this.#startX == null && this.#endX == null;
  }

  #getInitialBBox() {
    return [0, 0, this.parentDimensions[0], this.parentDimensions[1]];
  }

  #setStroke() {
    // 先不调整stroke看看效果怎么样
  }

  /**
   * Start to draw on the canvas.
   * @param {number} x
   * @param {number} y
   */
  #startDrawing(x, y) {
    this.drawingIsBegin = true;

    this.#startX = x;
    this.#startY = y;

    this.canvas.addEventListener("contextmenu", noContextMenu);
    this.canvas.addEventListener("pointerleave", this.#boundCanvasPointerleave);
    this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove);
    this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup);

    // 按下的一瞬间，就不需要再remove了
    this.canvas.removeEventListener(
      "pointerdown",
      this.#boundCanvasPointerdown
    );

    this.isEditing = true;
    if (!this.#isCanvasInitialized) {
      this.#isCanvasInitialized = true;
      this.#setCanvasDims();
    }

    this.#setStroke();

    // 先注释掉，且只留一行 看看会不会有什么影响
    // window.requestAnimationFrame(this.#requestFrameCallback);
  }

  /**
   * Draw on the canvas.
   * @param {number} x
   * @param {number} y
   */
  #redrawArrow(x, y) {
    this.#endX = x;
    this.#endY = y;
    this.#doRedrawArrow();
  }

  #doRedrawArrow() {
    const startX = this.#startX;
    const startY = this.#startY;
    const endX = this.#endX;
    const endY = this.#endY;

    const ctx = this.ctx;
    const canvas = this.canvas;
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 箭头的角度
    const angle = Math.atan2(endY - startY, endX - startX);
    const arrowSize = 10;

    // 绘制直线
    ctx.beginPath();
    ctx.moveTo(startX, startY);
    ctx.lineTo(endX, endY);
    ctx.stroke();

    // 绘制箭头
    ctx.beginPath();
    ctx.moveTo(
      endX - arrowSize * Math.cos(angle - Math.PI / 6),
      endY - arrowSize * Math.sin(angle - Math.PI / 6)
    );
    ctx.lineTo(endX, endY);
    ctx.lineTo(
      endX - arrowSize * Math.cos(angle + Math.PI / 6),
      endY - arrowSize * Math.sin(angle + Math.PI / 6)
    );
    ctx.closePath();
    ctx.fill();
  }

  /**
   * Stop to draw on the canvas.
   * @param {number} x
   * @param {number} y
   */
  #stopDrawing(x, y) {
    x = Math.min(Math.max(x, 0), this.canvas.width);
    y = Math.min(Math.max(y, 0), this.canvas.height);

    this.#redrawArrow(x, y);
  }

  /**
   * Redraw all the paths.
   */
  #redraw() {
    this.#doRedrawArrow();
  }

  /**
   * Commit the curves we have in this editor.
   */
  commit() {
    if (this.#disableEditing) {
      return;
    }

    super.commit();

    this.isEditing = false;
    this.disableEditMode();

    // This editor must be on top of the main ink editor.
    this.setInForeground();

    this.#disableEditing = true;
    this.div.classList.add("disabled");

    this.#fitToContent(/* firstTime = */ true);

    this.select();

    this.parent.addArrowEditorIfNeeded(/* isCommitting = */ true);

    // When commiting, the position of this editor is changed, hence we must
    // move it to the right position in the DOM.
    this.moveInDOM();
    this.div.focus({
      preventScroll: true /* See issue #15744 */,
    });
  }

  /** @inheritdoc */
  focusin(event) {
    if (!this._focusEventsAllowed) {
      return;
    }
    super.focusin(event);
    this.enableEditMode();
  }

  /**
   * onpointerdown callback for the canvas we're drawing on.
   * @param {PointerEvent} event
   */
  canvasPointerdown(event) {
    if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) {
      return;
    }

    // We want to draw on top of any other editors.
    // Since it's the last child, there's no need to give it a higher z-index.
    this.setInForeground();

    event.preventDefault();

    if (!this.div.contains(document.activeElement)) {
      this.div.focus({
        preventScroll: true /* See issue #17327 */,
      });
    }

    this.#startDrawing(event.offsetX, event.offsetY);
  }

  /**
   * onpointermove callback for the canvas we're drawing on.
   * @param {PointerEvent} event
   */
  canvasPointermove(event) {
    event.preventDefault();
    this.#redrawArrow(event.offsetX, event.offsetY);
  }

  /**
   * onpointerup callback for the canvas we're drawing on.
   * @param {PointerEvent} event
   */
  canvasPointerup(event) {
    event.preventDefault();
    this.#endDrawing(event);
    this.focusout(event);
    if (this.drawingIsBegin) {
      this._uiManager.hook.postInitialize(this);
    }
  }

  /**
   * onpointerleave callback for the canvas we're drawing on.
   * @param {PointerEvent} event
   */
  canvasPointerleave(event) {
    this.canvasPointerup(event);
  }

  /**
   * 结束绘制
   */
  #endDrawing(event) {
    this.canvas.removeEventListener(
      "pointerleave",
      this.#boundCanvasPointerleave
    );
    this.canvas.removeEventListener(
      "pointermove",
      this.#boundCanvasPointermove
    );
    this.canvas.removeEventListener("pointerup", this.#boundCanvasPointerup);

    // Slight delay to avoid the context menu to appear (it can happen on a long
    // tap with a pen).
    if (this.#canvasContextMenuTimeoutId) {
      clearTimeout(this.#canvasContextMenuTimeoutId);
    }
    this.#canvasContextMenuTimeoutId = setTimeout(() => {
      this.#canvasContextMenuTimeoutId = null;
      this.canvas.removeEventListener("contextmenu", noContextMenu);
    }, 10);

    this.#stopDrawing(event.offsetX, event.offsetY);

    this.addToAnnotationStorage();

    // Since the ink editor covers all of the page and we want to be able
    // to select another editor, we just put this one in the background.
    this.setInBackground();
  }

  /**
   * Create the canvas element.
   */
  #createCanvas() {
    this.canvas = document.createElement("canvas");
    this.canvas.width = this.canvas.height = 0;
    this.canvas.className = "arrowEditorCanvas";

    this.div.append(this.canvas);
    this.ctx = this.canvas.getContext("2d");
  }

  /**
   * Create the resize observer.
   */
  #createObserver() {
    this.#observer = new ResizeObserver(entries => {
      const rect = entries[0].contentRect;
      if (rect.width && rect.height) {
        this.setDimensions(rect.width, rect.height);
      }
    });
    this.#observer.observe(this.div);
  }

  /** @inheritdoc */
  get isResizable() {
    return !this.isEmpty() && this.#disableEditing;
  }

  needsToKeepAspectRatio() {
    return (
      ArrowEditor.ARROW_TYPE.QUADRANT_ONE == this.arrowType ||
      ArrowEditor.ARROW_TYPE.QUADRANT_TWO == this.arrowType ||
      ArrowEditor.ARROW_TYPE.QUADRANT_THREE == this.arrowType ||
      ArrowEditor.ARROW_TYPE.QUADRANT_FOUR == this.arrowType
    );
  }

  resizePoints() {
    switch (this.arrowType) {
      case ArrowEditor.ARROW_TYPE.ORIGIN_POINT:
      case ArrowEditor.ARROW_TYPE.HORIZONTAL_LEFT_TO_RIGHT:
      case ArrowEditor.ARROW_TYPE.HORIZONTAL_RIGHT_TO_LEFT:
        return ["middleRight", "middleLeft"];
      case ArrowEditor.ARROW_TYPE.VERTICAL_BTN_TO_TOP:
      case ArrowEditor.ARROW_TYPE.VERTICAL_TOP_TO_BTN:
        return ["topMiddle", "bottomMiddle"];
      case ArrowEditor.ARROW_TYPE.QUADRANT_ONE:
      case ArrowEditor.ARROW_TYPE.QUADRANT_TWO:
      case ArrowEditor.ARROW_TYPE.QUADRANT_THREE:
      case ArrowEditor.ARROW_TYPE.QUADRANT_FOUR:
        return ["topLeft", "topRight", "bottomRight", "bottomLeft"];
    }
  }

  /** @inheritdoc */
  render() {
    if (this.div) {
      return this.div;
    }

    super.render();
    this.div.setAttribute("data-l10n-id", "pdfjs-ink");
    const [x, y, w, h] = this.#getInitialBBox();

    // 默认情况下，整个页面都是画板
    // 但是如果是直接渲染的，就不是了
    // 设置宽高，位置
    if (!this.fromCommand) {
      this.setAt(x, y, 0, 0);
      this.setDims(w, h);
    }

    this.#createCanvas();

    if (this.fromCommand) {
      // 如果是直接渲染的话，走这边的逻辑
      this.#isCanvasInitialized = true;
      const [parentWidth, parentHeight] = this.parentDimensions;
      // 需要保持再保持
      if (this.needsToKeepAspectRatio()) {
        this.setAspectRatio(
          this.width * parentWidth,
          this.height * parentHeight
        );
      }
      // 复制来的需要偏移位置，不复制来的不用偏移
      this.setAt(this.x * parentWidth, this.y * parentHeight, 0, 0);
      this.#setCanvasDims();
      this.#formatStartEndPoints();
      this.setDims(this.width * parentWidth, this.height * parentHeight);
      this.#redraw();
      this.div.classList.add("disabled");
    } else {
      this.div.classList.add("editing");
      this.enableEditMode();
    }
    this.#createObserver();

    return this.div;
  }

  #formatStartEndPoints() {
    const arrowType = this.arrowType;
    let height = this.canvas.height;
    let width = this.canvas.width;
    if (arrowType == null || !height || !width || height < 10 || width < 10) {
      return;
    }
    height = height - 2 * ArrowEditor._offset_for_arrow;
    width = width - 2 * ArrowEditor._offset_for_arrow;
    const TYPE = ArrowEditor.ARROW_TYPE;
    const that = this;
    function set_point(sx, sy, ex, ey) {
      that.#startX = sx;
      that.#startY = sy;
      that.#endX = ex;
      that.#endY = ey;
    }
    // 原点
    switch (arrowType) {
      // 原点就按照从左到右来
      case TYPE.ORIGIN_POINT:
      case TYPE.HORIZONTAL_LEFT_TO_RIGHT:
        set_point(0, 0, width, 0);
        break;
      case TYPE.VERTICAL_BTN_TO_TOP:
        set_point(0, height, 0, 0);
        break;
      case TYPE.VERTICAL_TOP_TO_BTN:
        set_point(0, 0, 0, height);
        break;

      case TYPE.HORIZONTAL_RIGHT_TO_LEFT:
        set_point(width, 0, 0, 0);
        break;
      case TYPE.QUADRANT_ONE:
        set_point(0, height, width, 0);
        break;
      case TYPE.QUADRANT_TWO:
        set_point(width, height, 0, 0);
        break;
      case TYPE.QUADRANT_THREE:
        set_point(width, 0, 0, height);
        break;
      case TYPE.QUADRANT_FOUR:
        set_point(0, 0, width, height);
        break;
      default:
        throw new Error("unknown type " + arrowType);
    }
    this.#startX += 5;
    this.#startY += 5;
    this.#endX += 5;
    this.#endY += 5;
  }

  #setCanvasDims() {
    if (!this.#isCanvasInitialized) {
      return;
    }
    const [parentWidth, parentHeight] = this.parentDimensions;
    this.canvas.width = Math.ceil(this.width * parentWidth);
    this.canvas.height = Math.ceil(this.height * parentHeight);
  }

  /**
   * When the dimensions of the div change the inner canvas must
   * renew its dimensions, hence it must redraw its own contents.
   * @param {number} width - the new width of the div
   * @param {number} height - the new height of the div
   * @returns
   */
  setDimensions(width, height) {
    const roundedWidth = Math.round(width);
    const roundedHeight = Math.round(height);
    if (
      this.#realWidth === roundedWidth &&
      this.#realHeight === roundedHeight
    ) {
      return;
    }

    this.#realWidth = roundedWidth;
    this.#realHeight = roundedHeight;

    this.canvas.style.visibility = "hidden";

    const [parentWidth, parentHeight] = this.parentDimensions;
    this.width = width / parentWidth;
    this.height = height / parentHeight;
    this.fixAndSetPosition();

    if (this.#disableEditing) {
      this.#setScaleFactor(width, height);
    }

    this.#setCanvasDims();
    this.#formatStartEndPoints();
    this.#redraw();

    this.canvas.style.visibility = "visible";

    // For any reason the dimensions couldn't be in percent but in pixels, hence
    // we must fix them.
    this.fixDims();
  }

  fixAndSetPosition() {
    super.fixAndSetPosition();
  }

  postFixAndSetPosition() {
    const [width, height] = this.parentDimensions;
    this.#rawX = (this.x * width + ArrowEditor._offset_for_arrow) / width;
    this.#rawY = (this.y * height + ArrowEditor._offset_for_arrow) / height;
  }

  #setScaleFactor(width, height) {
    const padding = this.#getPadding();
    const scaleFactorW = (width - padding) / this.#baseWidth;
    const scaleFactorH = (height - padding) / this.#baseHeight;
    this.scaleFactor = Math.min(scaleFactorW, scaleFactorH);
  }

  static doFromPDFCoordinates(points, rect, rotation) {
    return this.#fromPDFCoordinates(points, rect, rotation);
  }

  static #fromPDFCoordinates(points, rect, rotation) {
    const [blX, blY, trX, trY] = rect;

    switch (rotation) {
      case 0:
        for (let i = 0, ii = points.length; i < ii; i += 2) {
          points[i] -= blX;
          points[i + 1] = trY - points[i + 1];
        }
        break;
      case 90:
        for (let i = 0, ii = points.length; i < ii; i += 2) {
          const x = points[i];
          points[i] = points[i + 1] - blY;
          points[i + 1] = x - blX;
        }
        break;
      case 180:
        for (let i = 0, ii = points.length; i < ii; i += 2) {
          points[i] = trX - points[i];
          points[i + 1] -= blY;
        }
        break;
      case 270:
        for (let i = 0, ii = points.length; i < ii; i += 2) {
          const x = points[i];
          points[i] = trY - points[i + 1];
          points[i + 1] = trX - x;
        }
        break;
      default:
        throw new Error("Invalid rotation");
    }
    return points;
  }

  /**
   * Get the bounding box containing all the paths.
   * @returns {Array<number>}
   */
  #getBbox() {
    const startX = this.#startX;
    const startY = this.#startY;
    const endX = this.#endX;
    const endY = this.#endY;

    // [xMin, yMin, xMax, yMax];
    return [
      Math.min(startX, endX),
      Math.min(startY, endY),
      Math.max(startX, endX),
      Math.max(startY, endY),
    ];
  }

  /**
   * The bounding box is computed with null thickness, so we must take
   * it into account for the display.
   * It corresponds to the total padding, hence it should be divided by 2
   * in order to have left/right paddings.
   * @returns {number}
   */
  #getPadding() {
    return 0;
  }

  getArrowAngle() {
    const startX = this.#startX;
    const startY = this.#startY;
    const endX = this.#endX;
    const endY = this.#endY;
    return Math.atan2(endY - startY, endX - startX);
  }

  setDims(width, height) {
    const [parentWidth, parentHeight] = this.parentDimensions;
    this.div.style.width = `${((100 * width) / parentWidth).toFixed(4)}%`;
    if (!this.getKeepAspectRatio()) {
      this.div.style.height = `${((100 * height) / parentHeight).toFixed(4)}%`;
    }
  }

  // 上下左右必须各增加5px
  #fitToContent() {
    if (this.isEmpty()) {
      return;
    }

    if (!this.#disableEditing) {
      this.#redraw();
      return;
    }

    const bbox = this.#getBbox();

    // this.#baseWidth = bbox[2] - bbox[0] + 2 * ArrowEditor._offset_for_arrow;
    // this.#baseHeight = bbox[3] - bbox[1] + 2 * ArrowEditor._offset_for_arrow;

    this.#displayWidth = bbox[2] - bbox[0] + 2 * ArrowEditor._offset_for_arrow;
    this.#displayHeight = bbox[3] - bbox[1] + 2 * ArrowEditor._offset_for_arrow;

    const width = Math.ceil(this.#displayWidth * this.scaleFactor);
    const height = Math.ceil(this.#displayHeight * this.scaleFactor);

    // 原始高度
    const rawWidth = Math.ceil((bbox[2] - bbox[0]) * this.scaleFactor);
    const rawHeight = Math.ceil((bbox[3] - bbox[1]) * this.scaleFactor);

    const [parentWidth, parentHeight] = this.parentDimensions;
    this.width = width / parentWidth;
    this.height = height / parentHeight;

    // 记录原始的 未经转变的 宽高
    this.#rawWidth = rawWidth / parentWidth;
    this.#rawHeight = rawHeight / parentHeight;
    this.#rawX = bbox[0] / parentWidth;
    this.#rawY = bbox[1] / parentHeight;

    this.adjustOffset();

    if (this.needsToKeepAspectRatio()) {
      this.setAspectRatio(width, height);
    }

    this.#setCanvasDims();
    this.#redraw();

    this.setDims(width, height);

    // 将位置从0移动到这里来
    this.translate(
      bbox[0] - ArrowEditor._offset_for_arrow,
      bbox[1] - ArrowEditor._offset_for_arrow
    );
  }

  postResizeEditor() {
    const [width, height] = this.parentDimensions;
    this.#rawX = (this.x * width + ArrowEditor._offset_for_arrow) / width;
    this.#rawY = (this.y * height + ArrowEditor._offset_for_arrow) / height;
    this.#rawWidth =
      (this.width * width - 2 * ArrowEditor._offset_for_arrow) / width;
    this.#rawHeight =
      (this.height * height - 2 * ArrowEditor._offset_for_arrow) / height;
  }

  adjustOffset() {
    const degree = (180 * this.getArrowAngle()) / Math.PI;
    const startX = this.#startX;
    const startY = this.#startY;
    const endX = this.#endX;
    const endY = this.#endY;
    const offset = ArrowEditor._offset_for_arrow;

    // 箭头所在象限
    if (degree >= 0 && degree < 90) {
      this.#startX = 0;
      this.#startY = 0;
      this.#endX = endX - startX;
      this.#endY = endY - startY;
    } else if (degree >= 90 && degree <= 180) {
      this.#startX = startX - endX;
      this.#startY = 0;
      this.#endX = 0;
      this.#endY = endY - startY;
    } else if (degree < 0 && degree >= -90) {
      this.#startX = 0;
      this.#startY = startY - endY;
      this.#endX = endX - startX;
      this.#endY = 0;
    } else if (degree < -90 && degree >= -180) {
      this.#startX = startX - endX;
      this.#startY = startY - endY;
      this.#endX = 0;
      this.#endY = 0;
    }

    // f final 经过最终调整后的 x y的值
    const f = {
      startX: this.#startX,
      startY: this.#startY,
      endX: this.#endX,
      endY: this.#endY,
    };

    let arrowType = null;

    const TYPE = ArrowEditor.ARROW_TYPE;
    // 全部都是 0
    if (f.startX == 0 && f.startY == 0 && f.endX == 0 && f.endY == 0) {
      arrowType = TYPE.ORIGIN_POINT;
    }
    // 从上到下的直线
    else if (f.startX == 0 && f.startY == 0 && f.endX == 0) {
      arrowType = TYPE.VERTICAL_TOP_TO_BTN;
    }
    // 从下到上的直线
    else if (f.startX == 0 && f.endX == 0 && f.endY == 0) {
      arrowType = TYPE.VERTICAL_BTN_TO_TOP;
    }
    // 从左到右的直线
    else if (f.startX == 0 && f.startY == 0 && f.endY == 0) {
      arrowType = TYPE.HORIZONTAL_LEFT_TO_RIGHT;
    }
    // 从右到左的直线
    else if (f.startY == 0 && f.endX == 0 && f.endY == 0) {
      arrowType = TYPE.HORIZONTAL_RIGHT_TO_LEFT;
    }
    // 起点在左上角 —— 第四象限
    else if (f.startX == 0 && f.startY == 0) {
      arrowType = TYPE.QUADRANT_FOUR;
    }
    // 起点在右上角 —— 第三象限
    else if (f.startY == 0 && f.endX == 0) {
      arrowType = TYPE.QUADRANT_THREE;
    }
    // 起点在右下角 —— 第二象限
    else if (f.endX == 0 && f.endY == 0) {
      arrowType = TYPE.QUADRANT_TWO;
    }
    // 起点左下角 —— 第一象限
    else if (f.startX == 0 && f.endY == 0) {
      arrowType = TYPE.QUADRANT_ONE;
    }

    this.arrowType = arrowType;
    this.#startX += offset;
    this.#startY += offset;
    this.#endX += offset;
    this.#endY += offset;
  }

  /** @inheritdoc */
  static deserialize(data, parent, uiManager) {
    throw new Error("unable to deserialize");
  }

  // fixFromBbox(width, height) {
  //   const bbox = this.#getBbox();
  //   this.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);
  //   this.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);
  //   this.#setScaleFactor(width, height);
  // }

  static doBuildPath2D(path) {
    console.log("无需再build了");
  }

  /** @inheritdoc */
  serialize(isForCopying = false) {
    throw new Error("不支持序列化与反序列化");
  }
}

export { ArrowEditor };
